<?php
//
//  VictoryCMS - Content managment system and framework.
//  Copyright (C) 2009  Lewis Gunsch
//
//  This file is part of VictoryCMS.
//
//  VictoryCMS is free software: you can redistribute it and/or modify
//  it under the terms of the GNU General Public License as published by
//  the Free Software Foundation, either version 3 of the License, or
//  (at your option) any later version.
//
//  VictoryCMS is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//  GNU General Public License for more details.
//
//  You should have received a copy of the GNU General Public License
//  along with VictoryCMS.  If not, see <http://www.gnu.org/licenses/>.

/**
 * VictoryCMS - Permission
 *
 * @license http://www.gnu.org/licenses/gpl.html
 * @author Lewis Gunsch
 * @filesource
 * @package Models
 */

/**
 * This class represents a permission level for a page,
 * component, or a user in the VictoryCMS.
 *
 * @package Models
 *
 * @todo Convert documentation to javadocs style
 * @todo Needs a PHPUnit testing class
 * @todo Permissions should be managed by database
 * with default guest and admin permissions.
 **/
class Permission implements DBManager {

	/**
	 * Permission levels list, default values are admin
	 * and anonymous
	 **/
	protected $permissionList = array(
	/*
	 * These are needed in case the database is not set up yet.
	 * They will be added to the database when it's created.
	 */
		1=>"admin",
		100=>"anonymous"
	);

	/**
	 * Current permission level, -1 when not set.
	 **/
	private $currentPermission = -1;

	/**
	 * Constructs a new permission level with the given permission.
	 * This should be one of permissionList's keys or values, otherwise an
	 * PermissionException is thrown.
	 *
	 * @exception PermissionException
	 *
	 * @see $permissionList
	 *
	 * @param Number or String The permission level or number
	 *
	 **/
	public function __construct( $permissionLevel = -1) {

		// get the permission list from the database
		$queries = new PermissionQuery();
		$db = VcmsDBFactory::createVcmsDb();

		/*
		 *  Check if the database is valid: exists with right fields.
		 *  If it is valid then copy in user created permission roles.
		 */
		$valid = $this->isValid();
		if ($valid == true) {
			// copy in client created permission roles, also overriding default values
			while(($row = $db->fetchRow())) {
				$this->permissionList[$row['permission_id']] = $row['permission_name'];
			}
			$db->runQuery('get_all_asc', $queries);
		}

		// -1 is not a valid permission, it represents uninitialized
		if ($permissionLevel != -1) {
			$this->setPermission( $permissionLevel );
		} else {
			$this->currentPermission = -1;
		}
		$db->cleanUp();
	}


	/**
	 * Returns the current permission level in numeric format.
	 *
	 * @return Number permission level number.
	 **/
	public function getPermission() {

		return $this->currentPermission;
	}


	/**
	 * Returns an associative array of permission levels and names,
	 * with levels as numberic keys and their respective permission level
	 * string format.
	 *
	 * @return Array Associative array of permission names and value
	 **/
	public function getPermissionList() {

		// return a copy, not the original!
		$copy = $this->permissionList;
		return $copy;
	}


	/**
	 * Set the current permission, this should be one
	 * of permissionList's keys or values, otherwise an
	 * PermissionException is thrown.
	 *
	 * @exception PermissionException
	 *
	 * @see $permissionList
	 *
	 * @param String or Number The permission level number or string, one from permissionList
	 **/
	public function setPermission( $permissionLevel ) {

		// Numeric $permissionLevel
		if ( is_int($permissionLevel) && key_exists( $permissionLevel, $this->permissionList) ) {
			$this->currentPermission = $permissionLevel;

			// string $permissionLevel
		} else {
			$key = array_search($permissionLevel, $this->permissionList);
			if ( ! empty($key) ) {
				$this->currentPermission = $key;
			} else {
				throw new PermissionException;
			}
		}
	}

	/**
	 * Returns an associative array of permission levels and names in
	 * ascending order, lowest permission first. Permission levels as
	 * numberic keys and their respective permission level string format.
	 * It is returned sorted with the lowest permission first.
	 *
	 * @return Array Associative array of permission names and value sorted ascending order
	 */
	public function getPermissionListAsc() {

		// get a copy
		$ascList = $this->permissionList;
		krsort($ascList, SORT_NUMERIC);
		return $ascList;
	}

	/**
	 * Returns an associative array of permission levels and names in
	 * descending order, highest permission first. Permission levels as
	 * numberic keys and their respective permission level string format.
	 * It is returned sorted with the highest permission first.
	 *
	 * @return Array Associative array of permission names and value sorted descending order
	 */
	public function getPermissionListDesc() {

		// get a copy
		$ascList = $this->permissionList;
		ksort($ascList, SORT_NUMERIC);
		return $ascList;
	}

	/**
	 * Returns true if $permissionLevel <= to the current permission level,
	 * otherwise false. If the current permission level has not been set
	 * then false is always returned. A lower permission level
	 * has higher priveledges. This will accept both the string
	 * or level number. If the belowFlag is set to true then
	 * the compare will be reversed; true will be returned if the
	 * permission is greater than or equal to the $permissionLevel.
	 *
	 *
	 * @exception PermissionException
	 *
	 * @param Number,String,Permission The permission level number, string, or Permission
	 * @param Boolean Flag for changing the compare to <=
	 *
	 * @return Boolean True if $permissionLevel is >= the current permission level
	 **/
	public function comparePermission( $permissionLevel, $belowflag = false ) {

		/*
		 * if permissionLevel is a Permission object convert it
		 * to numeric format
		 */
		if( $permissionLevel instanceof Permission ) {
			$temp = $permissionLevel;
			unset($permissionLevel);
			$permissionLevel = $temp->getPermission();
			unset($temp);
		}

		// current permission is not set, so always false
		if ( $this->currentPermission == -1 ) {
			if ($belowflag) {
				return true;
			} else {
				return false;
			}
		}

		// Numeric $permissionLevel
		if ( is_numeric($permissionLevel) && key_exists( $permissionLevel, $this->permissionList) ) {
			if ($belowflag) {
				return (($permissionLevel >= $this->currentPermission )? true : false);
			} else {
				return (($permissionLevel <= $this->currentPermission )? true : false);
			}

			// string $permissionLevel
		} else {
			$key = array_search($permissionLevel, $this->permissionList);
			if ( ! empty($key) ) {
				if ($belowflag) {
					$returnVal = (($key >= $this->currentPermission )? true : false);
				} else {
					$returnVal = (($key <= $this->currentPermission )? true : false);
				}
			} else {
				throw new PermissionException;
			}
		}

	}

	/**
	 * @see include/database_abstraction_layer/DBManager#setUpDb()
	 */
	public function setUpDb() {
		$queries = new PermissionQuery();
		$db = VcmsDBFactory::createVcmsDb();

		$db->runQuery('create_table', $queries);

		// default permission values
		foreach($this->permissionList as $key => $value) {
			if (!is_int($key) && $key >= 0) {
				throw new PermissionKeyException;
			}
			$db->runQuery('add_permission', $queries, array($key, $value));
		}
		//$db->runQuery('add_permission', $queries, array(100,"anonymous"));

		//$db->runQuery('add_permission', $queries, array(11,"team admin"));
		//$db->runQuery('add_permission', $queries, array(21,"team leader"));
		//$db->runQuery('add_permission', $queries, array(41,"general user"));

		$db->cleanUp();
	}

	/**
	 * @see include/database_abstraction_layer/DBManager#updateDb()
	 */
	public function updateDb() {
		// no changes for this version of Permission from the previous version
	}

	/**
	 * @see include/database_abstraction_layer/DBManager#tearDownDb()
	 */
	public function tearDownDb() {
		$queries = new PermissionQuery();
		$db = VcmsDBFactory::createVcmsDb();

		$db->runQuery('drop_table', $queries);

		$db->cleanUp();
	}

	/**
	 * @see include/database_abstraction_layer/DBManager#isValid()
	 */
	public function isValid() {
		$queries = new PermissionQuery();
		$db = VcmsDBFactory::createVcmsDb();
		$db->runQuery('check_exists', $queries);
		$rowCount = $db->fetchNumRows();

		$db->cleanUp();
		if ($rowCount == 1) {
			return true;
		} else {
			return false;
		}
	}

}
?>
